#include "Item_Point.h"
#include "Metadata_Point.h"
#include "Parser.h"

#include <string>
#include <algorithm>
#include <math.h>
#include "Log.h"

Item_Point::Item_Point() {
    LOG_ENTER(OBJECT,"Item_Point::Item_Point");

    coords = NULL;
    nCoords = 0;

    LOG_EXIT();
}

Item_Point::~Item_Point () {
    LOG_ENTER(OBJECT,"Item_Point::~Item_Point ");

    if (coords != NULL)
        delete[] coords;

    LOG_EXIT();
}

void Item_Point::parse(Parser& parser,
                       const Metadata& fInfo,
                       std::string& name) {
    LOG_ENTER(OBJECT,"Item_Point::parse");

    Metadata_Point& info = (Metadata_Point&) fInfo;
    nCoords = info.dimension;
    coords = new double[nCoords];

    LOG(OBJECT) << "nCoords=" << nCoords;

    parser.next_line();
    int iToken = 0;
    int iCoord = 0;
    double x;
    while (parser.match_double(x)) {

        LOG(OBJECT) << "  iToken=" << iToken
                    << ". parser matched x=" << x;

        if (iCoord >= nCoords) {
            LOG(ERROR) << "Item_Point.Parse: too many coords!";
            throw std::string("Item_Point.Parse: too many coords!");
        }
        if (iToken == info.name_coord) {

            LOG(OBJECT) << "it's the name";

            parser.backup(1);
            parser.match_any_word(name);
        }
        else if (find(info.skip_coords.begin(), info.skip_coords.end(), iToken)
                 == info.skip_coords.end()) {

            LOG(OBJECT) << "it's a regular coordinate";

            coords[iCoord++] = x;
        }
        iToken++;
    }

    LOG(OBJECT) << "coords[0]=" << coords[0]
                << " coords[1]=" << coords[1];
    LOG_EXIT();
}

std::ostream& Item_Point::print(std::ostream& os) const {
    LOG_ENTER(OBJECT,"Item_Point::print");
    LOG(OBJECT) << "print this=" << (void*)this;

    os << "[";
    for (int i=0; i<nCoords; i++)
        os << " " << coords[i];
    os << " ]";

    LOG_EXIT();
    return os;
}

bool Item_Point::operator==(const Metric_Object *other) const{
    LOG_ENTER(OBJECT,"Item_Point::operator==");

    for (int i=0; i<nCoords; i++) {
        if (coords[i] != ((Item_Point*)other)->coords[i]) {

            LOG_EXIT();
            return false;
        }
    }

    LOG_EXIT();
    return true;
}

double Item_Point::metric(const Metric_Object *other) const {
    LOG_ENTER(OBJECT,"Item_Point::distance");
    LOG(OBJECT) << "this=" << this << " other=" << other;

    double sum = 0;
    double *other_coords = ((Item_Point*)other)->coords;
    for (int i=0; i<nCoords; i++) {
        double dx = coords[i] - other_coords[i];
        sum += dx * dx;
    }

    LOG_EXIT();
    return sqrt(sum);
}

int Item_Point::get_dimension() {
    LOG_ENTER(OBJECT,"Item_Point::get_dimension");

    LOG_EXIT();
    return nCoords;
}

double Item_Point::operator[](const int i) const {
    return coords[i];
}

double& Item_Point::operator[](const int i) {
    return coords[i];
}

std::ostream& operator<<( std::ostream& os,
                          const Item_Point& p ) {
    return p.print(os);
}
